这是一篇半翻译半笔记式的文章,如果你之前对Common FileUpload了解不多,本文可以快速了解如何使用方法,如果你有时间,推荐你看官方文档
概述
FileUpload能够以多种不同的方式使用,具体取决于应用程序的要求。在最简单的情况下,您将调用单个方法来解析servlet请求,然后处理解析出来的Item集合。此外也可以自定义FileUpload以完全控制各个Item的存储方式,比如设置缓存目录、直接将接收到的Item以流的形式写入数据库等。
FileUpload依赖于Commons IO,因此类路径下要有Commons IO的jar包。当然采用Maven依赖的方式不用担心,maven会自动为我们下载Commons IO包
工作原理
FileUpload依据规范RFC1867中”基于表单的HTML文件上载”对上传的文件数据进行解析,解析出来的每个项目对应一个FileItem对象。
每个FileItem都有许多我们可能感兴趣的属性:获取contentType,获取原本的文件名,获取文件大小,获取FiledName(如果是表单域上传),判断是否在内存中,判断是否属于表单域等。
FileUpload使用FileItemFactory创建新的FileItem。该工厂可以控制每个项目的创建方式。目前提供的工厂实现可以将项目的数据存储临时存储在内存或磁盘上,具体取决于项目的大小(即数据字节,在指定的大小内时,存在内存中,超出范围,存在磁盘上)。
开始之前的判断
在处理上传项目前,最好是先解析一下当前请求是否属于文件上传请求,采用如下方式:
1 | //检查我们是否有文件上传请求 |
该方法的原理也很简单,就是获取request的contentType以判断是否是multipart,源码如下:
1 | public static final boolean isMultipartContent( |
1 | public static final String MULTIPART = "multipart/"; |
可用的最简配置
有两种方式能够接收上传文件
- 传统方式:首先创建FileItemFactory,创建ServletFileUpload时传入factory,再从upload对象获取FileItem,然后调用write(File)直接写入文件。这种方式将接收到的文件临时存储到内存或磁盘中,后续用户再进行处理,比较方便,但是占用时间和空间
- 流方式:直接ServletFileUpload中获取FileItem,再从FileItem中获取输入流,从流中直接接收数据,没有临时缓存这一步。使用没那么方便,但是比较节省时间和空间
如下演示两种方式
使用传统API
1 | // Create a factory for disk-based file items |
FileItemFctory可以设置的内容如下
1 | factory.setRepository(File dir); // 设置临时文件存储位置 |
ServletFileUpload可以设置的内容如下:
1 | upload.setMaxSize(long bytes); //设置整个请求的最大值,大于该值时,是不允许传送的 |
FileItem能够获取的内容如下:
1 | item.getContentType(); // 获取单个Item的ContentType |
使用流API
这种方式其实说来应该是最好的选择,因为他也并不麻烦,而且节省了缓存的空间和时间,在性能上是最好的选择。只需要按照如下方式使用即可
1 | // Create a new file upload handler |
文件上传进度监听器
当上传文件非常大时,进度监听器就能够排上用场了,使用方式也非常简单,创建监听器对象,设置到ServletFileUpload即可。
1 | ProgressListener listener = new ProgressListener() { |
此处有一个问题:监听器会被频繁调用,当其内部实现的逻辑较为简单时,可能无伤大雅,但当逻辑较为复杂或占用资源时,监听器就可能影响到程序的性能。
解决方案:以某种方式减少监听器内部逻辑执行的次数,比如下面这种方式
1 | ProgressListener progressListener = new ProgressListener(){ |
临时文件清理跟踪器
文件清理跟踪器仅适用于传统方式: 传统方式在处理文件之前将文件写入临时文件,这样的临时文件在我们上传任务完成时就成了垃圾,需要进行自动回收。Cmmons FileUpload当然也提供了这个功能,其运行原理:org.apache.commons.io.FileCleanerTracker开启一个收割线程,在DiskFileItem被垃圾回收器回收时,自动清理掉对应的临时文件。
要想开启文件自动清理功能,需要按照如下配置:
- 在Servlet中我们应该在web.xml中配置一个Servlet监听器
1
2
3
4
5<listener>
<listener-class>
org.apache.commons.fileupload.servlet.FileCleanerCleanup
</listener-class>
</listener> - 创建org.apache.commons.io.FileCleaningTracker对象,设置给DiskFileItemFactory。
1 | FileCleaningTracker fileCleaningTracker = FileCleanerCleanup.getFileCleaningTracker(context); |
- 然后就是按照我们之前学习的步骤正常操作即可
要想禁用临时文件自动删除功能,可将FileCleaningTracker设置为null(其实默认就为null)
1 | factory.setFileCleaningTracker(null); |